package org.lemon.db.store;

import java.nio.charset.StandardCharsets;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * @author l00805076
 * @since 2024-12-11 19:38
 */
public class LogManager {
    private FileManager fileManager;
    private String logFile;
    private Page logPage;
    private BlockId currentBlockId;
    private long latestLsn;
    private long latestSavedLsn;
    private Lock lock = new ReentrantLock();
    private static final int HEAD_LEN = 4;

    public LogManager(FileManager fileManager, String logFile) {
        this.fileManager = fileManager;
        this.logFile = logFile;
        this.logPage = Page.create(fileManager.getBlockSize());
        long logSize = fileManager.size(logFile);
        if(logSize == 0){
            this.currentBlockId = fileManager.append(logFile);
            this.logPage.setInt(0, fileManager.getBlockSize());
        } else {
            this.currentBlockId = new BlockId(logFile,logSize - 1);
            fileManager.read(this.currentBlockId,this.logPage);
        }
    }

    public void flushByLSN(long lsn){
        if(lsn <= latestSavedLsn){
            return;
        }
        flush();
        latestSavedLsn = lsn;
    }

    public void flush(){
        //将当前区块数据写入写入磁盘
        fileManager.write(currentBlockId,logPage);
    }

    public long append(String str){
        return append(str.getBytes(StandardCharsets.UTF_8));
    }

    public long append(byte[] record){
        lock.lock();
        try{
            int boundary = logPage.getInt(0);
            int bytesNeed = logPage.blen(record);
            if(boundary - bytesNeed < HEAD_LEN){
                flush();
                this.currentBlockId = appendNewBlock();
                boundary = logPage.getInt(0);
            }
            int writePos = boundary - bytesNeed;
            logPage.setBytes(writePos,record);
            logPage.setInt(0, writePos);
            latestLsn++;
        } finally {
            lock.unlock();
        }
        return latestLsn;
    }

    public BlockId appendNewBlock(){
        BlockId blockId = fileManager.append(logFile);
        logPage.setInt(0,fileManager.getBlockSize());
        logPage.reset();
        fileManager.write(blockId,logPage);
        logPage.reset();
        return blockId;
    }

    public LogIterator iterator(){
        flush();
        return new LogIterator(fileManager,currentBlockId);
    }
}
